package com.jyothsna.vendingMachine;

import static org.junit.Assert.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

/**
 * @author Jyothsna
 */
public class VendingMachineTest {
        public VendingMachine vMachine;
        public VendingMachineItem vItem;

        /**
         * @throws java.lang.Exception
         */
        @Before
        public void setUp() throws Exception {
                vMachine = new VendingMachine();
                vItem= new VendingMachineItem("Coke", 1.0);
        }

        /**
         * @throws java.lang.Exception
         */
        @After
        public void tearDown() throws Exception {
                vMachine = null;
                vItem = null;
        }

        /**
         * Test method for Adding an Item for Vending Machine Using AddItem method
         */
        @Test
        public final void testAddItem() {
                //empty slot
                assertNull(vMachine.getItem(VendingMachine.A_CODE));

                //Add a null item
                vMachine.addItem(null, VendingMachine.A_CODE);
                assertNull(vMachine.getItem(VendingMachine.A_CODE));

                //Item is added correctly
                vMachine.addItem(vItem, VendingMachine.A_CODE);
                assertSame(vItem, vMachine.getItem(VendingMachine.A_CODE));

                //check if the item is not in the wrong slot
                assertNull(vMachine.getItem(VendingMachine.B_CODE));
                assertNull(vMachine.getItem(VendingMachine.C_CODE));
                assertNull(vMachine.getItem(VendingMachine.D_CODE));

        }

        /**
         * If an item is added to a slot and the slot is already occupied,then
         *  VendingMachineException will be thrown
         */
        @Test(expected=VendingMachineException.class)
        public final void testSlotOccupied() {
                //Add multiple item in one slot,
                vMachine.addItem(vItem, VendingMachine.A_CODE);
                vMachine.addItem(vItem, VendingMachine.A_CODE);
        }

        /**
         * If you add an item with an invalid code,then VendingMachineException will be thrown
         */
        @Test(expected=VendingMachineException.class)
        public final void testAddInvalidCode() {
                //Add item with an invalid code
                vMachine.addItem(vItem, "slot");
        }

        /**
         * Test method for removing an Item from Vending Machine Using RemoveItem() method
         */
        @Test
        public final void testRemoveItem() {

                vMachine.addItem(vItem, VendingMachine.A_CODE);
                //Test if Item is removed correctly
                VendingMachineItem removedItem = vMachine.removeItem(VendingMachine.A_CODE);
                //Test if the removed item is a correct one, later other one occupies the slot with the given code.
                assertSame(vItem, removedItem);
                //slot is empty now
                assertNull(vMachine.getItem(VendingMachine.A_CODE));
        }

        /**
         * If you remove an item from the slot and the specified code is empty then
         * VendingMachineException will be thrown
         */
        @Test(expected=VendingMachineException.class)
        public final void testRemoveEmptySlot() {
                //remove item from an empty slot
                vMachine.removeItem(VendingMachine.A_CODE);
        }

        /**
         * Test method for removing Invalid Code
         * If you remove item from the slot with invalid code,
         * VendingMachineException will be thrown
         */
        @Test(expected=VendingMachineException.class)
        public final void testRemoveInvalidCode() {
                //remove item from the slot with invalid code
                vMachine.removeItem(null);
        }

        /**
         * If you insert money, balance should be the previous balance amount.
         */
        @Test
        public final void testInsertMoney() {
                //get current balance
                double balance = vMachine.balance;
                //insert amount==0
                vMachine.insertMoney(0);
                //balance now should be the previous balance + amount.
                assertEquals(balance, vMachine.balance, 0.01);

                //insert amount>0
                vMachine.insertMoney(200.1);
                //balance now should be the previous balance + amount.
                assertEquals(balance+200.1, vMachine.balance, 0.01);

        }
        @Test(expected=VendingMachineException.class)
        public final void testInsertNegativeMoney() {
                //insert amount<0
                vMachine.insertMoney(-10);
        }

        /**
         * Test method for to get the balance from getBalance()
         */
        @Test
        public final void testGetBalance() {
                //vending machine should start with a 0 balance
                assertEquals(0, vMachine.getBalance(), 0.0001);
                //test balance > 0
                vMachine.insertMoney(200.1);
                double balance = vMachine.balance;
                //vending machine should return the amount of balance
                assertEquals(balance, vMachine.getBalance(), 0.01);
                //balance should remains the same as it was before the function was called.
                assertEquals(balance, vMachine.balance, 0.01);

        }

        /**
         * Test method for making a Purchase
         * Added many test cases for different scenarios
         */
        @Test
        public final void testMakePurchase() {

                vMachine.addItem(vItem, VendingMachine.A_CODE);

                //test if there is no enough money
                assertFalse(vMachine.makePurchase(VendingMachine.A_CODE));

                //test if money == item.price
                vMachine.insertMoney(vItem.getPrice());
                //Returns true if there is enough money to make the purchase
                assertTrue(vMachine.makePurchase(VendingMachine.A_CODE));
                //test if the amount of the item is subtracted from the balance
                assertEquals(0, vMachine.balance, 0.01);
                //test if the item is removed
                assertNull(vMachine.getItem(VendingMachine.A_CODE));

                //test if money > item.price
                vMachine.addItem(vItem, VendingMachine.B_CODE);
                vMachine.insertMoney(vItem.getPrice()+2.0);
                //Returns true if there is enough money to make the purchase
                assertTrue(vMachine.makePurchase(VendingMachine.B_CODE));
                //The amount of the item's purchase is subtracted from the balance
                assertEquals(2.0, vMachine.balance, 0.01);
                //the item has been removed
                assertNull(vMachine.getItem(VendingMachine.B_CODE));


                //test if the slot is empty
                assertFalse(vMachine.makePurchase(VendingMachine.B_CODE));

                //test if invalid slot code is punched in
                assertFalse(vMachine.makePurchase("test"));

                //test null slot code
                assertFalse(vMachine.makePurchase(null));

        }

        /**
         * Test method for returning back the Change()}.
         */
        @Test
        public final void testReturnChange() {
                //test when balance=0
                assertEquals(0, vMachine.returnChange(), 0.01);
                //test when balance > 0
                vMachine.addItem(vItem, VendingMachine.A_CODE);
                vMachine.insertMoney(100);
                vMachine.makePurchase(VendingMachine.A_CODE);
                //Returns: The amount of change in the machine
                assertEquals(100-vItem.getPrice(), vMachine.returnChange(), 0.01);
                //test when balance = 0
                assertEquals(0, vMachine.balance, 0.01);
        }

}
